/**
* @file lis302dl.cpp
* @author MCD Application Team and Clemens Zangl
* @date 28.10.2012
* @version V1.1.0
*
* (c) 2011 2011 STMicroelectronics
* (c) 2012 University of Applied Sciences Karlsruhe
*
* Definition to lis302dl.h
** 
*/

#include "lis302dl.h"


__IO uint32_t  LIS302DLTimeout = LIS302DL_FLAG_TIMEOUT;   


#define READWRITE_CMD              ((uint8_t)0x80) 

#define MULTIPLEBYTE_CMD           ((uint8_t)0x40)


// Dummy Byte Send by the SPI Master device in order to generate the Clock to the Slave device
#define DUMMY_BYTE                 ((uint8_t)0x00)


/**
Initializes the low level interface used to drive the LIS302DL
*/
static void LIS302DL_LowLevel_Init(void);

/**
Sends a Byte through the SPI interface and return the Byte received from the SPI bus.
\param [in] spi The SPI interface used to access the LIS302DL.
\param [in] byte Byte to send.
\return The received byte
*/
static uint8_t LIS302DL_SendByte(SPIInterface *spi, uint8_t byte);



void LIS302DL_Init(SPIInterface *spi, LIS302DL_InitTypeDef *LIS302DL_InitStruct)
{
  uint8_t ctrl = 0x00;
  
  // Configure the low level interface
  LIS302DL_LowLevel_Init();
  
  // Configure MEMS: data rate, power mode, full scale, self test and axes 
  ctrl = (uint8_t) (LIS302DL_InitStruct->Output_DataRate | LIS302DL_InitStruct->Power_Mode | \
                    LIS302DL_InitStruct->Full_Scale | LIS302DL_InitStruct->Self_Test | \
                    LIS302DL_InitStruct->Axes_Enable);
  
  // Update configuration to MEMS CTRL_REG1 regsister
  LIS302DL_Write(spi, &ctrl, LIS302DL_CTRL_REG1_ADDR, 1);
}


void LIS302DL_FilterConfig(SPIInterface *spi, LIS302DL_FilterConfigTypeDef *LIS302DL_FilterConfigStruct)
{
  uint8_t ctrl = 0x00;
  
  /* Read CTRL_REG2 register */
  LIS302DL_Read(spi, &ctrl, LIS302DL_CTRL_REG2_ADDR, 1);
  
  /* Clear high pass filter cut-off level, interrupt and data selection bits*/
  ctrl &= (uint8_t)~(LIS302DL_FILTEREDDATASELECTION_OUTPUTREGISTER | \
                     LIS302DL_HIGHPASSFILTER_LEVEL_3 | \
                     LIS302DL_HIGHPASSFILTERINTERRUPT_1_2);
  /* Configure MEMS high pass filter cut-off level, interrupt and data selection bits */                     
  ctrl |= (uint8_t)(LIS302DL_FilterConfigStruct->HighPassFilter_Data_Selection | \
                    LIS302DL_FilterConfigStruct->HighPassFilter_CutOff_Frequency | \
                    LIS302DL_FilterConfigStruct->HighPassFilter_Interrupt);
  
  /* Write value to MEMS CTRL_REG2 register */
  LIS302DL_Write(spi, &ctrl, LIS302DL_CTRL_REG2_ADDR, 1);
}


void LIS302DL_InterruptConfig(SPIInterface *spi, LIS302DL_InterruptConfigTypeDef *LIS302DL_IntConfigStruct)
{
  uint8_t ctrl = 0x00;
  
  /* Read CLICK_CFG register */
  LIS302DL_Read(spi, &ctrl, LIS302DL_CLICK_CFG_REG_ADDR, 1);
  
  /* Configure latch Interrupt request, click interrupts and double click interrupts */                   
  ctrl = (uint8_t)(LIS302DL_IntConfigStruct->Latch_Request| \
                   LIS302DL_IntConfigStruct->SingleClick_Axes | \
                   LIS302DL_IntConfigStruct->DoubleClick_Axes);
  
  /* Write value to MEMS CLICK_CFG register */
  LIS302DL_Write(spi, &ctrl, LIS302DL_CLICK_CFG_REG_ADDR, 1);
}


void LIS302DL_LowpowerCmd(SPIInterface *spi, uint8_t LowPowerMode)
{
  uint8_t tmpreg;
  
  /* Read CTRL_REG1 register */
  LIS302DL_Read(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
  
  /* Set new low power mode configuration */
  tmpreg &= (uint8_t)~LIS302DL_LOWPOWERMODE_ACTIVE;
  tmpreg |= LowPowerMode;
  
  /* Write value to MEMS CTRL_REG1 regsister */
  LIS302DL_Write(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
}

void LIS302DL_DataRateCmd(SPIInterface *spi, uint8_t DataRateValue)
{
  uint8_t tmpreg;
  
  /* Read CTRL_REG1 register */
  LIS302DL_Read(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
  
  /* Set new Data rate configuration */
  tmpreg &= (uint8_t)~LIS302DL_DATARATE_400;
  tmpreg |= DataRateValue;
  
  /* Write value to MEMS CTRL_REG1 regsister */
  LIS302DL_Write(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
}

void LIS302DL_FullScaleCmd(SPIInterface *spi, uint8_t FS_value)
{
  uint8_t tmpreg;
  
  /* Read CTRL_REG1 register */
  LIS302DL_Read(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
  
  /* Set new full scale configuration */
  tmpreg &= (uint8_t)~LIS302DL_FULLSCALE_9_2;
  tmpreg |= FS_value;
  
  /* Write value to MEMS CTRL_REG1 regsister */
  LIS302DL_Write(spi, &tmpreg, LIS302DL_CTRL_REG1_ADDR, 1);
}

void LIS302DL_RebootCmd(SPIInterface *spi)
{
  uint8_t tmpreg;
  /* Read CTRL_REG2 register */
  LIS302DL_Read(spi, &tmpreg, LIS302DL_CTRL_REG2_ADDR, 1);
  
  /* Enable or Disable the reboot memory */
  tmpreg |= LIS302DL_BOOT_REBOOTMEMORY;
  
  /* Write value to MEMS CTRL_REG2 regsister */
  LIS302DL_Write(spi, &tmpreg, LIS302DL_CTRL_REG2_ADDR, 1);
}

void LIS302DL_Write(SPIInterface *spi, uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
{
  /* Configure the MS bit: 
       - When 0, the address will remain unchanged in multiple read/write commands.
       - When 1, the address will be auto incremented in multiple read/write commands.
  */
  if(NumByteToWrite > 0x01)
  {
    WriteAddr |= (uint8_t)MULTIPLEBYTE_CMD;
  }
  /* Set chip select Low at the start of the transmission */
  LIS302DL_CS_LOW();
  
  /* Send the Address of the indexed register */
  LIS302DL_SendByte(spi, WriteAddr);
  /* Send the data that will be written into the device (MSB First) */
  while(NumByteToWrite >= 0x01)
  {
    LIS302DL_SendByte(spi, *pBuffer);
    NumByteToWrite--;
    pBuffer++;
  }
  
  /* Set chip select High at the end of the transmission */ 
  LIS302DL_CS_HIGH();
}

void LIS302DL_Read(SPIInterface *spi, uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead)
{  
  if(NumByteToRead > 0x01)
  {
    ReadAddr |= (uint8_t)(READWRITE_CMD | MULTIPLEBYTE_CMD);
  }
  else
  {
    ReadAddr |= (uint8_t)READWRITE_CMD;
  }
  /* Set chip select Low at the start of the transmission */
  LIS302DL_CS_LOW();
  
  /* Send the Address of the indexed register */
  LIS302DL_SendByte(spi, ReadAddr);
  
  /* Receive the data that will be read from the device (MSB First) */
  while(NumByteToRead > 0x00)
  {
    /* Send dummy byte (0x00) to generate the SPI clock to LIS302DL (Slave device) */
    *pBuffer = LIS302DL_SendByte(spi, DUMMY_BYTE);
    NumByteToRead--;
    pBuffer++;
  }
  
  /* Set chip select High at the end of the transmission */ 
  LIS302DL_CS_HIGH();
}

void LIS302DL_ReadACC(SPIInterface *spi, int32_t* out)
{
  uint8_t buffer[6];
  uint8_t crtl, i = 0x00;
   
  LIS302DL_Read(spi, &crtl, LIS302DL_CTRL_REG1_ADDR, 1);  
  LIS302DL_Read(spi, buffer, LIS302DL_OUT_X_ADDR, 6);
  
  switch(crtl & 0x20) 
    {
    /* FS bit = 0 ==> Sensitivity typical value = 18milligals/digit*/ 
    case 0x00:
      for(i=0; i<0x03; i++)
      {
        *out =(int32_t)(LIS302DL_SENSITIVITY_2_3G *  (int8_t)buffer[2*i]);
        out++;
      }
      break;
    /* FS bit = 1 ==> Sensitivity typical value = 72milligals/digit*/ 
    case 0x20:
      for(i=0; i<0x03; i++)
      {
        *out =(int32_t)(LIS302DL_SENSITIVITY_9_2G * (int8_t)buffer[2*i]);
        out++;
      }         
      break;
    default:
      break;
    }
 }

static void LIS302DL_LowLevel_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

	 /* Enable CS  GPIO clock */
  RCC_AHB1PeriphClockCmd(LIS302DL_SPI_CS_GPIO_CLK, ENABLE);
  
  /* Enable INT1 GPIO clock */
  RCC_AHB1PeriphClockCmd(LIS302DL_SPI_INT1_GPIO_CLK, ENABLE);
  
  /* Enable INT2 GPIO clock */
  RCC_AHB1PeriphClockCmd(LIS302DL_SPI_INT2_GPIO_CLK, ENABLE);

  /* Configure GPIO PIN for Lis Chip select */
  GPIO_InitStructure.GPIO_Pin = LIS302DL_SPI_CS_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(LIS302DL_SPI_CS_GPIO_PORT, &GPIO_InitStructure);

  /* Deselect : Chip Select high */
  GPIO_SetBits(LIS302DL_SPI_CS_GPIO_PORT, LIS302DL_SPI_CS_PIN);
  
  /* Configure GPIO PINs to detect Interrupts */
  GPIO_InitStructure.GPIO_Pin = LIS302DL_SPI_INT1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  GPIO_Init(LIS302DL_SPI_INT1_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = LIS302DL_SPI_INT2_PIN;
  GPIO_Init(LIS302DL_SPI_INT2_GPIO_PORT, &GPIO_InitStructure);
}

static uint8_t LIS302DL_SendByte(SPIInterface *spi, uint8_t byte)
{
  /* Loop while DR register in not emplty */
  LIS302DLTimeout = LIS302DL_FLAG_TIMEOUT;
  
	FlagStatus flagStatus;
	
	while (0 == spi->getFlagStatus(SPI_I2S_FLAG_TXE, flagStatus) && RESET == flagStatus)
  {
    if((LIS302DLTimeout--) == 0) return LIS302DL_TIMEOUT_UserCallback();
  }
  
  /* Send a Byte through the SPI peripheral */
  spi->sendByte(byte);
  //SPI_I2S_SendData(SPI1, byte);
  
  /* Wait to receive a Byte */
  LIS302DLTimeout = LIS302DL_FLAG_TIMEOUT;
  
	while (0 == spi->getFlagStatus(SPI_I2S_FLAG_RXNE, flagStatus) && RESET == flagStatus)
  {
    if((LIS302DLTimeout--) == 0) return LIS302DL_TIMEOUT_UserCallback();
  }
  
  /* Return the Byte read from the SPI bus */
  uint8_t readByte;

  spi->readByte(readByte);
  return readByte;
}

#ifdef USE_DEFAULT_TIMEOUT_CALLBACK
/**
  * @brief  Basic management of the timeout situation.
  * @param  None.
  * @retval None.
  */
uint32_t LIS302DL_TIMEOUT_UserCallback(void)
{
  /* Block communication and all processes */
  while (1)
  {   
  }
}
#endif 

